﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace PPSkin
{
    public partial class PPTreeView : TreeView
    {
        #region 属性定义

        private Font rootFont = new Font("微软雅黑", 12);
        private Font nodeFont = new Font("微软雅黑", 11);
        private Color rootTextColor = Color.DodgerBlue;
        private Color rootBackColor = ColorTranslator.FromHtml("#333333");
        private Color nodeTextColor = Color.White;
        private Color nodeBackColor = ColorTranslator.FromHtml("#333333");
        private Color nodeBorderColor = Color.Gray;
        private Color nodeMouseInBackColor = Color.DimGray;
        private Color nodeSelectTextColor = Color.White;
        private Color nodeSelectBackColor = Color.DimGray;
        private Color nodeSelectBorderColor = Color.Gray;
        private Color lineColor = ColorTranslator.FromHtml("#333333");
        private Point iconOffset = new Point(0, 0);
        private Size iconSize = new Size(30, 30);

        [Description("根节点字体"), Category("自定义")]
        public Font RootFont
        {
            get { return rootFont; }
            set
            {
                rootFont = value;
                this.Invalidate();
            }
        }

        [Description("子节点字体"), Category("自定义")]
        public Font NodeFont
        {
            get { return nodeFont; }
            set
            {
                nodeFont = value;
                this.Invalidate();
            }
        }

        [Description("根节点文本颜色"), Category("自定义")]
        public Color RootTextColor
        {
            get { return rootTextColor; }
            set
            {
                rootTextColor = value;
                this.Invalidate();
            }
        }

        [Description("根节点背景色"), Category("自定义")]
        public Color RootBackColor
        {
            get { return rootBackColor; }
            set
            {
                rootBackColor = value;
                this.Invalidate();
            }
        }

        [Description("子节点文本颜色"), Category("自定义")]
        public Color NodeTextColor
        {
            get { return nodeTextColor; }
            set
            {
                nodeTextColor = value;
                this.Invalidate();
            }
        }

        [Description("子节点背景色"), Category("自定义")]
        public Color NodeBackColor
        {
            get { return nodeBackColor; }
            set
            {
                nodeBackColor = value;
                this.Invalidate();
            }
        }

        [Description("子节点边框色"), Category("自定义")]
        public Color NodeBorderColor
        {
            get { return nodeBorderColor; }
            set
            {
                nodeBorderColor = value;
                this.Invalidate();
            }
        }

        [Description("子节点鼠标悬停背景色"), Category("自定义")]
        public Color NodeMouseInBackColor
        {
            get { return nodeMouseInBackColor; }
            set
            {
                nodeMouseInBackColor = value;
                this.Invalidate();
            }
        }

        [Description("子节点选中文本色"), Category("自定义")]
        public Color NodeSelectTextColor
        {
            get { return nodeSelectTextColor; }
            set
            {
                nodeSelectTextColor = value;
                this.Invalidate();
            }
        }

        [Description("子节点选中背景色"), Category("自定义")]
        public Color NodeSelectBackColor
        {
            get { return nodeSelectBackColor; }
            set
            {
                nodeSelectBackColor = value;
                this.Invalidate();
            }
        }

        [Description("子节点选中边框色"), Category("自定义")]
        public Color NodeSelectBorderColor
        {
            get { return nodeSelectBorderColor; }
            set
            {
                nodeSelectBorderColor = value;
                this.Invalidate();
            }
        }

        [Description("子节点选中左侧条颜色"), Category("自定义")]
        public Color NodeSelectBarColor { get; set; } = Color.DodgerBlue;

        [Description("根节点与子节点连接线颜色"), Category("自定义")]
        public new Color LineColor
        {
            get { return lineColor; }
            set
            {
                lineColor = value;
                this.Invalidate();
            }
        }

        [Description("根节点图标的位置偏移"), Category("自定义")]
        public Point IconOffset
        {
            get { return iconOffset; }
            set
            {
                iconOffset = value;
                this.Invalidate();
            }
        }

        [Description("根节点图标的大小"), Category("自定义")]
        public Size IconSize
        {
            get { return iconSize; }
            set
            {
                iconSize = value;
                this.Invalidate();
            }
        }

        [Description("自定义图片列表"), Category("自定义")]
        public ImageList PPImageList { get; set; } = null;

        [Description("是否显示checkbox"), Category("自定义")]
        public bool ShowCheckBox { get; set; } = false;

        #endregion 属性定义

        public drawMode TextDrawMode { get; set; } = drawMode.Anti;

        public enum drawMode
        {
            Anti,
            Clear
        }

        public PPTreeView()
        {
            InitializeComponent();

            base.SetStyle(
            ControlStyles.DoubleBuffer |
            ControlStyles.OptimizedDoubleBuffer |
            ControlStyles.AllPaintingInWmPaint |
            ControlStyles.ResizeRedraw |
            ControlStyles.SupportsTransparentBackColor, true);
            base.UpdateStyles();
            DoubleBuffered = true;
            this.DrawMode = TreeViewDrawMode.OwnerDrawAll;
            this.HotTracking = true;
            this.CheckBoxes = false;
            this.ImageList = null;
            this.ShowPlusMinus = false;
            this.FullRowSelect = false;
        }

        /// <summary>
        /// 重写CreateParams方法 解决控件过多加载闪烁问题
        /// </summary>
        //protected override CreateParams CreateParams
        //{
        //    get
        //    {
        //        CreateParams cp = base.CreateParams;
        //        cp.ExStyle |= 0x02000000;//用双缓冲绘制窗口的所有子控件
        //        return cp;
        //    }
        //}

        /// <summary>
        /// SizeChange导致treeNode闪屏
        /// </summary>
        private const int TVM_SETEXTENDEDSTYLE = 0x112C;

        private const int TVS_EX_DOUBLEBUFFER = 0x0004;

        private void UpdateExtendedStyles()
        {
            int Style = 0;

            if (DoubleBuffered)
                Style |= TVS_EX_DOUBLEBUFFER;

            if (Style != 0)
                Win32.SendMessage(Handle, TVM_SETEXTENDEDSTYLE, new IntPtr(TVS_EX_DOUBLEBUFFER), new IntPtr(Style));
        }

        protected override void OnHandleCreated(EventArgs e)
        {
            base.OnHandleCreated(e);
            UpdateExtendedStyles();
        }

        protected override void OnDrawNode(DrawTreeNodeEventArgs e)
        {
            if (e.Bounds.IsEmpty)
                return;

            e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
            e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;

            Bitmap bmp = new Bitmap(this.Width, e.Bounds.Height);
            Graphics g = Graphics.FromImage(bmp);
            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
            g.TextRenderingHint = TextDrawMode == drawMode.Anti ? System.Drawing.Text.TextRenderingHint.AntiAlias : System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;

            Image icon;
            if (!e.Node.IsExpanded)//根节点未展开
            {
                icon = PPSkinResources.rightBlue.Clone() as Bitmap;
            }
            else//根节点展开
            {
                icon = PPSkinResources.downBlue.Clone() as Bitmap;
            }
            if (e.Node.Level == 0)//根节点
            {
                try
                {
                    g.Clear(rootBackColor);
                    //重绘图标，一级节点X值默认为23，padding-left为6,17 = 23 - 6
                    if (e.Node.Nodes.Count > 0)
                    {
                        g.DrawImage(icon, bmp.Width - 20 - (isScrollShow ? 20 : 0), (bmp.Height - icon.Height) / 2);
                    }

                    if (ShowCheckBox)
                    {
                        Rectangle checkRect = new Rectangle(3, (bmp.Height - 15) / 2, 15, 15);
                        if (e.Node.Checked)
                        {
                            g.DrawImage(PPSkinResources.CheckNormal.Clone() as Bitmap, checkRect);
                        }
                        else
                        {
                            g.DrawImage(PPSkinResources.CheckSelect.Clone() as Bitmap, checkRect);
                        }
                    }

                    if (PPImageList != null && e.Node.ImageIndex != -1 && PPImageList.Images.Count - 1 >= e.Node.ImageIndex)
                    {
                        Rectangle imageRect = new Rectangle(new Point(iconOffset.X + (ShowCheckBox ? 18 : 0), (bmp.Height - IconSize.Height) / 2 + iconOffset.Y), IconSize);
                        g.DrawImage(this.PPImageList.Images[e.Node.ImageIndex], imageRect);
                    }

                    StringFormat stringFormat = new StringFormat();
                    stringFormat.Alignment = StringAlignment.Near;
                    stringFormat.LineAlignment = StringAlignment.Center;
                    stringFormat.Trimming = StringTrimming.EllipsisCharacter;
                    stringFormat.FormatFlags = StringFormatFlags.NoClip;
                    Brush brush = new SolidBrush(rootTextColor);

                    int boundLeft = (ShowCheckBox ? 18 : 0) + ((PPImageList != null) ? IconSize.Width : 0);
                    Rectangle rect = new Rectangle(boundLeft, 0, bmp.Width - boundLeft, bmp.Height);//文本显示区域

                    g.DrawString(e.Node.Text, rootFont, brush, rect, stringFormat);

                    brush.Dispose();
                    stringFormat.Dispose();
                }
                catch
                {
                }
            }
            else
            {
                if (!e.Node.Bounds.IsEmpty)//如果子节点的Bounds属性不为空(Empty），绘制该节点
                {
                    try
                    {
                        int space = e.Node.Level * 10;

                        Rectangle box = new Rectangle(space, 0, bmp.Width - space, bmp.Height);

                        if (e.Node.IsSelected)//二级节点被选中
                        {
                            g.Clear(nodeSelectBackColor);

                            Brush brush = new SolidBrush(NodeSelectBarColor);
                            g.FillRectangle(brush, 0, 0, 4, bmp.Height);
                            brush.Dispose();
                        }
                        else
                        {
                            if ((e.State & TreeNodeStates.Hot) != 0)//鼠标指针在二级节点上
                            {
                                g.Clear(nodeMouseInBackColor);
                            }
                            else
                            {
                                g.Clear(nodeBackColor);
                            }
                        }

                        if (ShowCheckBox)
                        {
                            Rectangle checkRect = new Rectangle(3 + space, (bmp.Height - 15) / 2, 15, 15);
                            if (e.Node.Checked)
                            {
                                g.DrawImage(PPSkinResources.CheckNormal.Clone() as Bitmap, checkRect);
                            }
                            else
                            {
                                g.DrawImage(PPSkinResources.CheckSelect.Clone() as Bitmap, checkRect);
                            }
                        }

                        if (PPImageList != null && e.Node.ImageIndex != -1 && PPImageList.Images.Count - 1 >= e.Node.ImageIndex)
                        {
                            Rectangle imageRect = new Rectangle(new Point(space + iconOffset.X + (ShowCheckBox ? 18 : 0), (bmp.Height - IconSize.Height) / 2 + iconOffset.Y), IconSize);
                            g.DrawImage(this.PPImageList.Images[e.Node.ImageIndex], imageRect);
                        }

                        StringFormat stringFormat = new StringFormat();
                        stringFormat.Alignment = StringAlignment.Near;
                        stringFormat.LineAlignment = StringAlignment.Center;
                        stringFormat.Trimming = StringTrimming.EllipsisCharacter;
                        stringFormat.FormatFlags = StringFormatFlags.NoClip;
                        Brush textbrush = new SolidBrush(e.Node.IsSelected ? nodeSelectTextColor : nodeTextColor);

                        int boundLeft = space + (ShowCheckBox ? 18 : 0) + ((PPImageList != null) ? IconSize.Width : 0);
                        Rectangle rect = new Rectangle(boundLeft, 0, bmp.Width - boundLeft, bmp.Height);//文本显示区域

                        g.DrawString(e.Node.Text, NodeFont, textbrush, rect, stringFormat);

                        if (e.Node.Nodes.Count > 0)
                        {
                            g.DrawImage(icon, this.Width - 20 - (isScrollShow ? 20 : 0), (bmp.Height - icon.Height) / 2);
                        }

                        //绘制连接线,30 = 20 + 10,10为图标宽的一半，保证topEnd点在图标中心，15为行高一半

                        //if (ShowRootLines)
                        //{
                        //    Point start = new Point(e.Node.Bounds.Left - 20, e.Node.Bounds.Top + ItemHeight / 2);
                        //    Point middle = new Point(e.Node.Bounds.Left - 40, e.Node.Bounds.Top + ItemHeight / 2);
                        //    Point topEnd = new Point(e.Node.Bounds.Left - 40, e.Node.Bounds.Top);
                        //    Point bottomEnd = new Point(e.Node.Bounds.Left - 40, e.Node.Bounds.Bottom);
                        //    Pen linePen = new Pen(lineColor);
                        //    e.Graphics.DrawLine(linePen, start, middle);
                        //    e.Graphics.DrawLine(linePen, middle, topEnd);
                        //    if (null != e.Node.NextNode)
                        //    {
                        //        e.Graphics.DrawLine(linePen, middle, bottomEnd);
                        //    }
                        //}
                    }
                    catch
                    {
                    }
                }
            }
            e.Graphics.DrawImage(bmp, 0, e.Bounds.Top);
            bmp.Dispose();
            g.Dispose();
        }

        protected override void OnMouseClick(MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                //base.OnMouseClick(e);
                TreeNode tn = this.GetNodeAt(e.Location);
                if (tn == null)
                    return;

                if (0 != tn.Level)//点击一级节点不使二级节点的选中效果消失
                {
                    this.SelectedNode = tn;
                }

                if (this.CheckBoxes)
                {
                    Rectangle checkRect = new Rectangle(2 + tn.Level * 10, tn.Bounds.Top + (tn.Bounds.Height - 15) / 2, 15, 15);
                    if (checkRect.Contains(e.Location))
                    {
                        tn.Checked = !tn.Checked;
                    }
                }

                //图标中心点向右的区域也能单击折叠与展开
                Rectangle bounds = new Rectangle(tn.Bounds.Left, tn.Bounds.Y, this.Width - tn.Bounds.Left, this.ItemHeight);
                if (tn != null && bounds.Contains(e.Location) == true)
                {
                    if (tn.IsExpanded == false)
                        tn.Expand();
                    else
                        tn.Collapse();
                }

                int h = 0;
                foreach (TreeNode node in this.Nodes)
                {
                    h += getheight(node);
                }

                if (h > this.Height)
                {
                    isScrollShow = true;
                }
                else
                {
                    isScrollShow = false;
                }
            }
        }

        private TreeNode currentNode = null;

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            TreeNode tn = this.GetNodeAt(e.Location);
            Graphics g = this.CreateGraphics();
            if (currentNode != tn)
            {
                //绘制当前节点的hover背景
                if (null != tn)
                {
                    OnDrawNode(new DrawTreeNodeEventArgs(g, tn, new Rectangle(0, tn.Bounds.Y, this.Width - 4, tn.Bounds.Height), TreeNodeStates.Hot));
                }

                //取消之前hover的节点背景
                if (null != currentNode)
                {
                    OnDrawNode(new DrawTreeNodeEventArgs(g, currentNode, new Rectangle(0, currentNode.Bounds.Y, this.Width - 4, currentNode.Bounds.Height), TreeNodeStates.Default));
                }
            }

            if (tn != null)
            {
                if (tn.Level == 1)//子节点鼠标变手
                {
                    this.Cursor = Cursors.Hand;
                }
                else
                {
                    this.Cursor = Cursors.Default;
                }
            }

            currentNode = tn;
            g.Dispose();//释放Graphics资源
        }

        protected override void OnMouseLeave(EventArgs e)
        {
            base.OnMouseLeave(e);
            //移出控件时取消Hover背景
            if (currentNode != null)
            {
                Graphics g = this.CreateGraphics();
                OnDrawNode(new DrawTreeNodeEventArgs(g, currentNode, new Rectangle(0, currentNode.Bounds.Y, this.Width - 4, currentNode.Bounds.Height), TreeNodeStates.Default));

                currentNode = null;//同一个节点Leave后Move有Hover效果
                g.Dispose();
            }
        }

        private bool isScrollShow = false;

        protected override void OnAfterExpand(TreeViewEventArgs e)
        {
            base.OnAfterExpand(e);
            int h = 0;
            foreach (TreeNode node in this.Nodes)
            {
                h += getheight(node);
            }

            if (h > this.Height)
            {
                isScrollShow = true;
            }
            else
            {
                isScrollShow = false;
            }
        }

        protected override void OnSizeChanged(EventArgs e)
        {
            base.OnSizeChanged(e);
            int h = 0;
            foreach (TreeNode node in this.Nodes)
            {
                h += getheight(node);
            }

            if (h > this.Height)
            {
                isScrollShow = true;
            }
            else
            {
                isScrollShow = false;
            }
        }

        private int getheight(TreeNode treeNode)
        {
            if (treeNode == null)
                return 0;
            int h = this.ItemHeight;
            foreach (TreeNode node in treeNode.Nodes)
            {
                if (node.IsVisible)
                {
                    h += getheight(node);
                }
            }
            return h;
        }
    }
}